<?php
/**
 * Description : PHP操作shell类
 */
class Shell
{
    protected static $error_log_path = ERROR_ROOT_PATH;	//报错日志目录
    protected static $exec_output = array();	//最后一次EXEC的输出信息

    //用Shell运行命令行$command，运行成功返回0或输出结果(SHELL_EXEC)，否则返回错误代码或TRUE(如果shell未执行)
    public static function run($command, $mode = 'EXEC')
    {
        $return_code = true;
        $output = '';
        if (is_string($command))
        {
            switch ($mode)
            {
                default:
                case 'EXEC':	//不输出结果，返回最后一行shell结果，所有结果可以保存到一个返回的数组里面
                    exec($command, self::$exec_output, $return_code);
                    break;

                case 'SHELL_EXEC':	//将完整的输出以字符串的方式返回
                    $output = shell_exec($command);
                    $return_code = FALSE;
                    break;

                case 'PASSTHRU':	//把命令的运行结果原样地直接输出到标准输出设备上
                    passthru($command, $return_code);
                    break;

                case 'SYSTEM':	//输出并返回最后一行shell结果
                    system($command, $return_code);
                    break;
            }

            if ($return_code)
            {
                self::err_log($return_code, $command);
            }
        }

        $result = ('SHELL_EXEC' == $mode) ? $output : $return_code;
        return $result;
    }

    //获取上次EXEC的输出
    public static function get_exec_output()
    {
        return self::$exec_output;
    }

    /* Linux 报错返回值含义:
        1:  Operation not permitted
        2:  No such file or directory
        3:  No such process
        4:  Interrupted system call
        5:  Input/output error
        6:  No such device or address
        7:  Argument list too long
        8:  Exec format error
        9:  Bad file descriptor
        10:  No child processes
        11:  Resource temporarily unavailable
        12:  Cannot allocate memory
        13:  Permission denied
        14:  Bad address
        15:  Block device required
        16:  Device or resource busy
        17:  File exists
        18:  Invalid cross-device link
        19:  No such device
        20:  Not a directory
        21:  Is a directory
        22:  Invalid argument
        23:  Too many open files in system
        24:  Too many open files
        25:  Inappropriate ioctl for device
        26:  Text file busy
        27:  File too large
        28:  No space left on device
        29:  Illegal seek
        30:  Read-only file system
        31:  Too many links
        32:  Broken pipe
        33:  Numerical argument out of domain
        34:  Numerical result out of range
        35:  Resource deadlock avoided
        36:  File name too long
        37:  No locks available
        38:  Function not implemented
        39:  Directory not empty
        40:  Too many levels of symbolic links
        42:  No message of desired type
        43:  Identifier removed
        44:  Channel number out of range
        45:  Level 2 not synchronized
        46:  Level 3 halted
        47:  Level 3 reset
        48:  Link number out of range
        49:  Protocol driver not attached
        50:  No CSI structure available
        51:  Level 2 halted
        52:  Invalid exchange
        53:  Invalid request descriptor
        54:  Exchange full
        55:  No anode
        56:  Invalid request code
        57:  Invalid slot
        59:  Bad font file format
        60:  Device not a stream
        61:  No data available
        62:  Timer expired
        63:  Out of streams resources
        64:  Machine is not on the network
        65:  Package not installed
        66:  Object is remote
        67:  Link has been severed
        68:  Advertise error
        69:  Srmount error
        70:  Communication error on send
        71:  Protocol error
        72:  Multihop attempted
        73:  RFS specific error
        74:  Bad message
        75:  Value too large for defined data type
        76:  Name not unique on network
        77:  File descriptor in bad state
        78:  Remote address changed
        79:  Can not access a needed shared library
        80:  Accessing a corrupted shared library
        81:  .lib section in a.out corrupted
        82:  Attempting to link in too many shared libraries
        83:  Cannot exec a shared library directly
        84:  Invalid or incomplete multibyte or wide character
        85:  Interrupted system call should be restarted
        86:  Streams pipe error
        87:  Too many users
        88:  Socket operation on non-socket
        89:  Destination address required
        90:  Message too long
        91:  Protocol wrong type for socket
        92:  Protocol not available
        93:  Protocol not supported
        94:  Socket type not supported
        95:  Operation not supported
        96:  Protocol family not supported
        97:  Address family not supported by protocol
        98:  Address already in use
        99:  Cannot assign requested address
        100:  Network is down
        101:  Network is unreachable
        102:  Network dropped connection on reset
        103:  Software caused connection abort
        104:  Connection reset by peer
        105:  No buffer space available
        106:  Transport endpoint is already connected
        107:  Transport endpoint is not connected
        108:  Cannot send after transport endpoint shutdown
        109:  Too many references: cannot splice
        110:  Connection timed out
        111:  Connection refused
        112:  Host is down
        113:  No route to host
        114:  Operation already in progress
        115:  Operation now in progress
        116:  Stale NFS file handle
        117:  Structure needs cleaning
        118:  Not a XENIX named type file
        119:  No XENIX semaphores available
        120:  Is a named type file
        121:  Remote I/O error
        122:  Disk quota exceeded
        123:  No medium found
        124:  Wrong medium type
        125:  Operation canceled
        126:  Required key not available
        127:  Key has expired
        128:  Key has been revoked
        129:  Key was rejected by service
        130:  Owner died
        131:  State not recoverable
    */

    //将Shell运行错误返回值和命令行记入报错日志，记录成功则返回记录的字节数，否则返回FALSE
    protected static function err_log($err_code, $command)
    {
        $result = false;
        $time = time();
        is_dir(self::$error_log_path) or mkdir(self::$error_log_path);
        $errlog_file = self::$error_log_path . 'shell_error_' . date('Y-m-d', $time) . '.log';
        if ($fp = fopen($errlog_file, 'a'))
        {
            $err_message = date('H:i:s Y-m-d', $time) . ', Error Code: ' . $err_code . ', Shell: ' . $command . ', PHP: ' . $_SERVER['PHP_SELF'] . "\n";
            $result = fwrite($fp, $err_message);
            fclose($fp);
        }
        return $result;
    }
}
?>